package com.mi.system.security;

import com.mi.common.security.service.MiUser;
import com.mi.system.mapper.UserMapper;
import com.mi.system.service.RoleMenuService;
import com.mi.system.vo.RoleVO;
import com.mi.system.vo.UserVO;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;

import static cn.hutool.core.text.CharSequenceUtil.format;

/**
 * User detail service
 *
 * @author jin_huaquan
 * @version 1.0
 * @date 2021/11/4 13:10
 */
@Service
@RequiredArgsConstructor
public class MiUserDetailService implements UserDetailsService {

    private final UserMapper userMapper;
    private final RoleMenuService roleMenuService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        UserVO user = userMapper.selectByUsername(username);

        return Optional.ofNullable(user)
                .map(this::getSecurityUser)
                .orElseThrow(() -> new UsernameNotFoundException(format("{} not found", username)));
    }

    /**
     * get security user
     *
     * @param user user info
     * @return security user
     */
    private MiUser getSecurityUser(UserVO user) {
        String[] authorities = mapToAuthorities(user.getRoles());

        return new MiUser(user.getUsername(), user.getPassword(), user.getEnabled() == 1,
                true, true, true,
                AuthorityUtils.createAuthorityList(authorities),
                user.getId(), user.getDeptId(), user.getMobile());
    }

    /**
     * map roles to authorities
     *
     * @param roles role list
     * @return authority array
     */
    private String[] mapToAuthorities(List<RoleVO> roles) {
        Set<String> permissionSet = new HashSet<>(16);

        Long[] roleIds = roles.stream()
                .map(role -> {
                    permissionSet.add(role.getRoleCode());

                    return role.getId();
                })
                .toArray(Long[]::new);

        permissionSet.addAll(roleMenuService.listMenuPermission(roleIds));

        return permissionSet.toArray(new String[0]);
    }
}
